/**************************************************************/
/*  This file is licensed from, and is a trade secret of:     */
/*                                                            */
/*    Mercury Computer Systems, Incorporated                  */
/*    199 Riverneck Road                                      */
/*    Chelmsford, Massachusetts 01824-2820                    */
/*    United States of America                                */
/*    Telephone + 1 978 256-1300                              */
/*                                                            */
/*  Refer to your Software License Agreement for provisions   */
/*  on use, duplication, or disclosure.                       */
/*  US. Government users and prime contractors are also       */
/*  governed by the following Data Rights Notice:             */
/*  Use, duplication or disclosure by the US. Government is   */
/*  subject to the provisions set forth in FAR 52.227-14 for  */
/*  restricted rights or other agency data rights provisions  */
/*  on commercial software and documentation, as applicable.  */
/*                                                            */
/*  This file: (a) was developed at private expense, and no   */
/*  part was developed with government funds, (b) is a        */
/*  trade secret of Mercury Computer Systems, Inc. for the    */
/*  purposes of the Freedom of Information Act, (c) is        */
/*  "commercial computer software" subject to limited         */
/*  utilization as provided in the above noted Software       */
/*  License Agreement and (d) is in all respects data         */
/*  belonging exclusively to Mercury Computer Systems, Inc..  */
/*                                                            */
/**************************************************************/

/******************************************************************/
/* Copyright (c) 1999-2003, Mercury Computer Systems, Inc.,       */
/* Chelmsford MA.,and all third party embedded software sources.  */
/* All rights reserved under the Copyright laws of US. and        */
/* international treaties.                                        */
/*                                                                */
/* Patent Pending                                                 */
/******************************************************************/


//$COPYRIGHT_END


#include <iostream>
#include "BaseAssemblyController.h"
#include "SCA_Types.h"

/*
 * This definition is required for the CRC reference SCA CF and other 
 * similar SCA CF's where all the property configuration is performed 
 * only via the assembly controller.
 */

#define CONFIGURE_VIA_ASM_CONTROLLER

#ifdef CONFIGURE_VIA_ASM_CONTROLLER
#include "BaseAssemblyControllerPropMappingDB.h"
#endif

extern "C" {
  BASEAC_EXPORT
  POA_CF::Resource *
  CreateBaseAssemblyController (const char * identifier)
  {
    BaseAssemblyController * AssemblyController;

    if (BaseAssemblyController::Create (AssemblyController, identifier) != success) {
      return 0;
    }

    return (POA_CF::Resource *) AssemblyController;
  }
}

// Helper function.
static
void
AppendProperty (CF::Properties & props, const CF::DataType & prop)
{
  int old_length = props.length ();
  props.length (old_length+1);
  props[old_length] = prop;
}

/*
 * ----------------------------------------------------------------------
 * BaseAssemblyController Object
 * ----------------------------------------------------------------------
 */

// Constructor
BaseAssemblyController::BaseAssemblyController (const char * identifier)
  : CF__Resource_impl (identifier)
{
  m_identifier = CORBA::string_dup (identifier);
  m_resHead = 0;
  m_myPort_impl = 0;
}

// Destructor
BaseAssemblyController::~BaseAssemblyController ()
{
  delete m_myPort_impl;

  CORBA::Boolean done = false;

  //clear the Resource list
  ResourceListNode * res_to_be_deleted = m_resHead;

  while (!done) {
    if (m_resHead == 0) {
      done = true;
    }
    else {
      res_to_be_deleted = m_resHead;
      m_resHead = m_resHead->_next;
      CORBA::string_free (res_to_be_deleted->_resName);
      CORBA::release (res_to_be_deleted->_resRef);
      delete res_to_be_deleted;
    }
  }

  vector_ptrs_cleanup (m_prop_mappings);
}

status
BaseAssemblyController::Create (BaseAssemblyController *& handle,
				const char * identifier)
{
  BaseAssemblyController * Temp = new BaseAssemblyController (identifier);

  if (!Temp) {
    handle = 0;
    return fail;
  }

  handle = Temp;
  return success;
}

CORBA::Object_ptr
BaseAssemblyController::getPort (const CORBA::Char* name)
  throw (CORBA::SystemException,
	 CF::PortSupplier::UnknownPort)
{
  string temp (name);

  if (temp == "BaseAssemblyControllerPin1") {
    if (!m_myPort_impl) {
      m_myPort_impl = new BaseAssemblyControllerPort (this);
      m_myPort_var = m_myPort_impl->_this ();
    }
  }
  else {
    throw CF::PortSupplier::UnknownPort();
  }

  return CF::Port::_duplicate (m_myPort_var);
}

void
BaseAssemblyController::configure (const CF::Properties& configProperties)
  throw (CORBA::SystemException,
	 CF::PropertySet::InvalidConfiguration,
	 CF::PropertySet::PartialConfiguration)
{
  unsigned long ii, jj;
  CF::Properties invalidProperties;
  bool error = false;
  bool some_success = false;
  ComponentPropertyQueue prop_queue;

#ifdef MCS_VERBOSE
  cout << "BaseAssemblyController: Configuring "
       << configProperties.length() << " properties."
       << endl;
#endif

  for (ii=0; ii<configProperties.length(); ++ii) {
    bool match = false;

#ifdef MCS_VERBOSE
    cout << "BaseAssemblyController: Configuring property \"" 
	 << configProperties[ii].id << "\"" << endl;
#endif

    for (jj=0; jj<m_prop_mappings.size(); ++jj) {
      PropertyMapping & prop = *(m_prop_mappings[jj]);

      // look for a property whose name matches the specified property
      if (strcmp(prop.m_assembly_propname.c_str(), 
		 configProperties[ii].id) == 0) {
	// See if the specified property type matches 
	if (prop.PropertyTypeMatch(configProperties[ii])) {
	  match = true;
	  prop_queue.QueuePropValues(prop, configProperties[ii]);
	  some_success = true;
	  break;
	} // if value type compare
      } // if name compare
    } // for this-> properties loop

    /*
     * Our current plan is to have one Base Assembly Controller that will 
     * work with a number of applications; therefore, there will be some 
     * properties of the BaseAssemblyController that aren't appropriate
     * for certain applications.
     */

#ifndef CONFIGURE_VIA_ASM_CONTROLLER
    // If we couldn't find the specified property, log error.
    if (!match) { // specified property name not found
      // Add this property to invalid list
      error = true;
      AppendProperty (invalidProperties, configProperties[ii]);
    }
#endif
  } // for input properties loop

  // Throw one of 2 exceptions depending on whether we had some success.
  if (error) {
    if (some_success) {
      CF::PropertySet::PartialConfiguration prop_ex;
      prop_ex.invalidProperties = invalidProperties;
      throw prop_ex;
    }
    else {
      CF::PropertySet::InvalidConfiguration prop_ex;
      prop_ex.invalidProperties = invalidProperties;
      throw prop_ex;
    }
  } // if (error)

  prop_queue.ConfigureComponentProps ();
} // End of BaseAssemblyController::configure()

void
BaseAssemblyController::query (CF::Properties & configProperties)
  throw (CORBA::SystemException,
	 CF::UnknownProperties)
{
  unsigned int ii, jj;
  CF::Properties invalidProperties;
  bool error = false;
  unsigned int num_cfg_props_in = configProperties.length();

  // Special case - if the input properties is an empty list, return all.
  if (num_cfg_props_in == 0) {
    num_cfg_props_in = m_prop_mappings.size ();
    configProperties.length (num_cfg_props_in);

    for (ii=0; ii<num_cfg_props_in; ++ii) {
      configProperties[ii].id = 
	CORBA::string_dup (m_prop_mappings[ii]->m_assembly_propname.c_str());
    }
  }

  for (ii=0; ii<num_cfg_props_in; ++ii) {
    bool match = false;

    for (jj=0; jj<m_prop_mappings.size(); ++jj) {
      PropertyMapping &prop = *(m_prop_mappings[jj]);
      if (strcmp (prop.m_assembly_propname.c_str(), 
		  configProperties[ii].id) == 0) {
	match = true;
	prop.GetPropValue (configProperties[ii]);
      } // if name compare
    } // for this-> properties loop

    // If we couldn't find the specified property, log error
    if (!match) { // specified property name not found
      error = true;
      AppendProperty (invalidProperties, configProperties[ii]);
    }
  } // for input properties loop

  if (error) {
    CF::UnknownProperties prop_ex;
    prop_ex.invalidProperties = invalidProperties;
    throw prop_ex;
  }
} // End of BaseAssemblyController::query()

void
BaseAssemblyController::initialize ()
  throw (CORBA::SystemException,
	 CF::LifeCycle::InitializeError)
{
  CF__Resource_impl::initialize();
}

void
BaseAssemblyController::start ()
  throw (CORBA::SystemException,
	 CF::Resource::StartError)
{
  CF__Resource_impl::start();
  ResourceListNode *resNode = m_resHead;

  while (resNode) {
#ifdef MCS_VERBOSE
    cout << "BaseAssemblyController: Starting Resource \""
	 << resNode->_resName << "\"." << endl;
#endif
    resNode->_resRef->start();
    resNode = resNode->_next;
  }
}

void
BaseAssemblyController::stop ()
  throw (CORBA::SystemException,
	 CF::Resource::StopError)
{
  CF__Resource_impl::stop();

  ResourceListNode *resNode = m_resHead;

  while (resNode) {
#ifdef MCS_VERBOSE
    cout << "BaseAssemblyController: Stopping Resource \""
	 << resNode->_resName << "\"." << endl;
#endif
    resNode->_resRef->stop();
    resNode = resNode->_next;
  }
}

// Internal method for keeping track of components we are managing.
status
BaseAssemblyController::addObject (CORBA::Object_ptr object,
				   const CORBA::Char * Id)
{
  ResourceListNode* tempResourceNode;
  CF::Resource_ptr tempResource = CF::Resource::_narrow(object);

  if (CORBA::is_nil (tempResource)) {
    return fail;
  }

#ifdef MCS_VERBOSE
  cout << "BaseAssemblyController: Adding Resource \""
       << tempResource->identifier()
       << "\"." << endl;
#endif

  /*
   * Add the new resource to the resource list.
   */

  tempResourceNode = new ResourceListNode ();
  tempResourceNode->_resRef = CF::Resource::_duplicate (tempResource);

  tempResourceNode->_next = m_resHead;            
  tempResourceNode->_resName = CORBA::string_dup (tempResourceNode->_resRef->identifier());
  tempResourceNode->_connId = CORBA::string_dup (Id);
  m_resHead = tempResourceNode;

#ifdef CONFIGURE_VIA_ASM_CONTROLLER
  /*
   * The following code is needed to run with the CRC SCARI
   * Core Framework.  It allows the Base Assembly Controller to
   * configure all of the component properties for the application.
   * This is the specific requirements of the SCA spec.
   * Many core frameworks don't need this since the core framework 
   * will allow configuring of the component properties directly,
   * not through the Assembly Controller
   */

  unsigned int ii;
  unsigned int n_maps = 
    sizeof(component_property_mappings)/sizeof(component_prop_mapping);

  for (ii=0; ii<n_maps; ++ii) {
    if (strcmp(tempResourceNode->_resName, 
	       component_property_mappings[ii].component_id) == 0) {
      component_prop_mapping & map_i = component_property_mappings[ii];
#ifdef MCS_VERBOSE
      cout << "BaseAssemblyController: Mapping component property \"" 
	   << map_i.prop_map.component_prop_id
	   << "\" to assembly controller property \"" 
	   << map_i.prop_map.assembly_prop_id
	   << "\"" << endl;
#endif

      AddPropertyMapping(map_i.prop_map.assembly_prop_id,
			 map_i.component_id,
			 map_i.prop_map.component_prop_id);
    }
  } // for loop
#endif

  SetPropertyMappingObjectReferences (tempResourceNode->_resRef);
  return success;
}

status
BaseAssemblyController::removeObject (const CORBA::Char * Id)
{
  if (!m_resHead) {
    return fail;
  }

#ifdef MCS_VERBOSE
  cout << "BaseAssemblyController: Removing resource \""
       << Id << "\"." << endl;
#endif

  ResourceListNode * tempResourceNode = m_resHead;
  bool found = false;

  // Check the first list element.
  if (strcmp (tempResourceNode->_connId, Id) == 0) {
    m_resHead = tempResourceNode->_next;
    delete (tempResourceNode);
    return success;
  }

  // Check the rest of the list.

  while (tempResourceNode->_next) {
    if (strcmp (tempResourceNode->_next->_connId, Id) == 0) {
      ResourceListNode* toBeDeleted = tempResourceNode->_next;
      tempResourceNode->_next = tempResourceNode->_next->_next;
      delete (toBeDeleted);
      found = true;
      break;
    }
    tempResourceNode = tempResourceNode->_next;
  }

  if (!found) {
    return fail;
  }

  return success;
}

// Internal method used to implement the waveform property mapping.
void
BaseAssemblyController::AddPropertyMapping (const char *assembly_property_name, 
					    const char *component_id,
					    const char *component_property_name)
{

  /*
   * First look to see if this assembly property has already been mapped.
   * If so, we will create a new target for that property.
   * Otherwise we will create a new mapping
   */

  std::vector<PropertyMapping *>::iterator pm_i;
  bool match_found = false;

  for (pm_i = m_prop_mappings.begin(); pm_i != m_prop_mappings.end(); pm_i++) {
    if (0 == strcmp (assembly_property_name, 
		     (*pm_i)->m_assembly_propname.c_str())) {
      (*pm_i)->AddPropertyTarget (component_id, component_property_name);
      match_found = true;
      break;
    }
  }

  if (!match_found) {
    // Need a new property mapping
    PropertyMapping * new_map = 
      new PropertyMapping (assembly_property_name,
			   component_id,
			   component_property_name);

    m_prop_mappings.push_back(new_map);
  }  // End of if (!match_found)
} // End of BaseAssemblyController::AddPropertyMapping()

// Set the resource references for any of the assembly property mappings
void
BaseAssemblyController::SetPropertyMappingObjectReferences (CF::Resource_ptr res)
{
  std::vector<PropertyMapping *>::iterator pm_i;

  for (pm_i = m_prop_mappings.begin(); pm_i != m_prop_mappings.end(); pm_i++) {
    PropertyMapping &prop_map = **pm_i;
    std::vector<PropertyMapTarget *>::iterator pmt_i;

    for (pmt_i = prop_map.m_targets.begin(); 
	 pmt_i != prop_map.m_targets.end(); pmt_i++) {
      if (0 == strcmp (res->identifier(), (*pmt_i)->m_component_id.c_str()))
	(*pmt_i)->m_res_ref = CF::Resource::_duplicate(res);
    }
  }
} // End of SetSetPropertyMappingObjectReferences()

/*
 * ----------------------------------------------------------------------
 * Base Assembly Controller Port
 * ----------------------------------------------------------------------
 */

// Constructor
BaseAssemblyControllerPort::BaseAssemblyControllerPort (BaseAssemblyController * parentAssemblyController)
{
  m_myParentAssemblyController = parentAssemblyController;
}

// Destructor
BaseAssemblyControllerPort::~BaseAssemblyControllerPort()
{
}

void
BaseAssemblyControllerPort::connectPort (CORBA::Object_ptr connection,
					 const CORBA::Char * connectionId)
  throw (CORBA::SystemException,
	 CF::Port::InvalidPort, 
	 CF::Port::OccupiedPort)
{
  status test = fail;

  if (m_myParentAssemblyController) {
    test = m_myParentAssemblyController->addObject (connection,connectionId);
  }

  if (test == fail) {
    throw CF::PortSupplier::UnknownPort();
  }
}

void
BaseAssemblyControllerPort::disconnectPort (const CORBA::Char* connectionId)
  throw (CORBA::SystemException,
	 CF::Port::InvalidPort)
{
  status test = fail;

  if (!m_myParentAssemblyController) {
    m_myParentAssemblyController->removeObject(connectionId);
  }

  if (test == fail) {
    throw CF::PortSupplier::UnknownPort();
  }
}
